package com.xiaobias.distributed.pan.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FileUtils {
    private static final Logger logger = LoggerFactory.getLogger(FileUtils.class);

    private static final String appPath = "/apps/备份小助手/" + DateUtil.today() + "/";

    private static final String fileManagerUrl = "https://pan.baidu.com/rest/2.0/xpan/file";

    private static final String getReadyFileUrl = "https://pan.baidu.com/rest/2.0/xpan/file";

    private static final String slicingUploadFileUrl = "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2";

    private static final String downLoueUrl = "https://pan.baidu.com/rest/2.0/xpan/multimedia";

    private static final String fileSearch = "https://pan.baidu.com/rest/2.0/xpan/file?method=search";

    @Setter
    private static String accessToken = "g";


    private static String cloudPath;

    private static final long unit = 4L;

    public static String upload(String panDir,String localDir, String fileName) {
        if (StringUtils.isEmpty(panDir)) {
            panDir = appPath;
        }
        String downUrl = null;
        List<File> separate = null;
        try {
            String absoluteFilePath = localDir + "/" + fileName;
            cloudPath = panDir + absoluteFilePath;
            File file = new File(absoluteFilePath);
            separate = separate(absoluteFilePath);
            StringBuilder md5s = new StringBuilder();
            if (separate.size() > 1) {
                md5s.append("[\"");
                for (int index = 0; index < separate.size(); index++) {
                    md5s.append(DigestUtil.md5Hex(separate.get(index)));
                    if (index != separate.size() - 1) {
                        md5s.append("\",\"");
                    }
                    logger.info("正在分片,{}{}", separate.get(index).toString(), index);
                }
                md5s.append("\"]");
            } else if (separate.size() == 1) {
                md5s.append("[\"");
                md5s.append(DigestUtil.md5Hex(separate.get(0)));
                md5s.append("\"]");
            }
            String precreate = precreate(cloudPath, Long.valueOf(file.length()), md5s.toString());
            logger.info("预上传{}", precreate);
            String uploadId = JSONUtil.parseObj(precreate).getStr("uploadid");
            String upload = upload(cloudPath, uploadId, separate);
            logger.info("分片上传{}", upload);
            String create = create(Long.valueOf(file.length()), uploadId, md5s.toString());
            logger.info("创建文件{}", create);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (CollectionUtil.isNotEmpty(separate) && separate.size() > 1) {
                for (File file : separate) {
                    FileUtil.del(file);
                }
            }
        }
        return downUrl;
    }


    public static String getDownUrl(String fileName, String dir) {
        String dlink = null;
        try {
            String search = CharSequenceUtil.format("{}&access_token={}&key={}&dir={}", "https://pan.baidu.com/rest/2.0/xpan/file?method=search", accessToken, fileName, dir);
            String fileSearch = HttpUtil.get(search);
            JSONObject jsonObject = new JSONObject(fileSearch);
            JSONArray list = jsonObject.getJSONArray("list");
            JSONObject listJSONObject = list.getJSONObject(Integer.valueOf(0));
            Long fs_id = listJSONObject.getLong("fs_id");
            String url = "https://pan.baidu.com/rest/2.0/xpan/multimedia?method=filemetas&access_token=" + accessToken + "&fsids=[" + fs_id + "]&dlink=1&thumb=1&dlink=1&extra=1&needmedia=1&detail=1";
            String s = HttpUtil.createGet(url)
                    .header("User-Agent", "pan.baidu.com")
                    .header("Host", "pan.baidu.com")
                    .header("Content-Type", "application/x-www-form-urlencoded")
                    .execute().body();
            JSONObject sJsonObject = new JSONObject(s);
            JSONArray jsonArray = sJsonObject.getJSONArray("list");
            JSONObject jsonObjectClient = jsonArray.getJSONObject(Integer.valueOf(0));
            dlink = jsonObjectClient.getStr("dlink");
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return dlink;
    }


    private static String create(Long size, String uploadid, String blockList) {
        String strURL = "https://pan.baidu.com/rest/2.0/xpan/file?method=create&access_token=" + accessToken;
        String params = "path=" + cloudPath + "&size=" + size + "&uploadid=" + uploadid + "&block_list=" + blockList + "&isdir=0";
        return open(strURL, params, "POST");
    }


    private static String upload(String filePath, String uploadid, List<File> files) {
        try {
            for (int i = 0; i < files.size(); i++) {
                File file = files.get(i);
                String url = "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?method=upload&access_token=" + accessToken + "&type=tmpfile&partseq=" + i + "&path=" + filePath + "&uploadid=" + uploadid;
                String res = sendFile(url, file);
                logger.info("正在上传分片文件【{}】，响应结果【{}】", file.getName(), res);
            }
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String precreate(String cloudPath, Long size, String blockList) {
        String strURL = "https://pan.baidu.com/rest/2.0/xpan/file?method=precreate&access_token=" + accessToken;
        String params = "path=" + cloudPath + "&size=" + size + "&autoinit=1&block_list=" + blockList + "&isdir=0";
        return open(strURL, params, "POST");
    }

    private static String open(String strURL, String params, String method) {
        try {
            URL url = new URL(strURL);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(true);
            connection.setRequestMethod(method);
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("Content-Type", "application/json");
            connection.connect();
            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8);
            out.append(params);
            out.flush();
            out.close();
            int length = connection.getContentLength();
            InputStream is = connection.getInputStream();
            if (length != -1) {
                byte[] data = new byte[length];
                byte[] temp = new byte[512];
                int destPos = 0;
                int readLen;
                while ((readLen = is.read(temp)) > 0) {
                    System.arraycopy(temp, 0, data, destPos, readLen);
                    destPos += readLen;
                }
                return new String(data, StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static List<File> separate(String from) {
        List<File> files = new ArrayList<>();
        File file = new File(from);
        long splitSize = 4194304L;
        if (file.length() < splitSize) {
            logger.info("文件小于单个分片大小，无需分片{}", Long.valueOf(file.length()));
            files.add(file);
            return files;
        }
        int count = (int) (file.length() / splitSize);
        for (int index = 0; index <= count; index++) {
            String outPath = file.getParent() + "/part/" + file.getName() + "." + index + ".part";
            try {
                FileInputStream in = new FileInputStream(file);
                try {
                    FileChannel inChannel = in.getChannel();
                    try {
                        FileOutputStream out = new FileOutputStream(FileUtil.touch(outPath));
                        try {
                            FileChannel outChannel = out.getChannel();
                            try {
                                if (index != count) {
                                    inChannel.transferTo(splitSize * index, splitSize, outChannel);
                                } else {
                                    inChannel.transferTo(splitSize * index, file.length() - splitSize * count, outChannel);
                                }
                                File outFile = new File(outPath);
                                files.add(outFile);
                                if (outChannel != null) outChannel.close();
                            } catch (Throwable throwable) {
                                if (outChannel != null) try {
                                    outChannel.close();
                                } catch (Throwable throwable1) {
                                    throwable.addSuppressed(throwable1);
                                }
                                throw throwable;
                            }
                            out.close();
                        } catch (Throwable throwable) {
                            try {
                                out.close();
                            } catch (Throwable throwable1) {
                                throwable.addSuppressed(throwable1);
                            }
                            throw throwable;
                        }
                        if (inChannel != null) inChannel.close();
                    } catch (Throwable throwable) {
                        if (inChannel != null) try {
                            inChannel.close();
                        } catch (Throwable throwable1) {
                            throwable.addSuppressed(throwable1);
                        }
                        throw throwable;
                    }
                    in.close();
                } catch (Throwable throwable) {
                    try {
                        in.close();
                    } catch (Throwable throwable1) {
                        throwable.addSuppressed(throwable1);
                    }
                    throw throwable;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return files;
    }


    private static String sendFile(String url, File file) {
        try {
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setContentType(ContentType.MULTIPART_FORM_DATA);
            builder.addBinaryBody("file", file);
            String body = "";
            CloseableHttpClient client = HttpClients.createDefault();
            String encodeBlank = URLUtil.encodeBlank(url);
            HttpPost httpPost = new HttpPost(encodeBlank);
            HttpEntity httpEntity = builder.build();
            httpPost.setEntity(httpEntity);
            CloseableHttpResponse response = client.execute((HttpUriRequest) httpPost);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                body = EntityUtils.toString(entity, "utf-8");
            }
            EntityUtils.consume(entity);
            response.close();
            return body;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String uploadDir(String panDir, String localDir) {
        List<String> list = new ArrayList<>();
        ArrayList<File> allFiles = new ArrayList<>();
        listFilesRecursively(new File(localDir), allFiles);
        for (File file : allFiles) {
            list.add(upload(panDir, file.getParent(), file.getName()));
        }
        return list.toString();
    }

    public static void listFilesRecursively(File directory, List<File> allFiles) {
        if (directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        // 如果是目录，则递归调用
                        listFilesRecursively(file, allFiles);
                    } else {
                        allFiles.add(file);
                    }
                }
            }
        } else {
            logger.warn("Provided path is not a directory: {}", directory.getAbsolutePath());
        }
    }
}